#!/bin/bash
# Kubernetes 单节点安装脚本（k3s，面向 CentOS 7/RHEL 7，systemd 环境）
# 目标：稳定、可重复、可配置、非交互。
# 主要特性：
# - 系统预检（root、systemd、依赖、内核模块、sysctl、SELinux 提示）
# - 非交互模式（K3S_ACTION、K3S_DISABLE_REGISTRY_MIRRORS 等）
# - kubectl 安装回退（yum 失败则提供 k3s-kubectl 包装器）
# - kubeconfig 持久化到 /etc/profile.d（对所有用户生效）
# - registries.yaml 可配置并自动生效
# - 健壮的端口/就绪性等待与诊断

set -Eeuo pipefail

log() { echo -e "$(date +'%F %T') | $*"; }
warn() { echo -e "$(date +'%F %T') | WARNING: $*" >&2; }
err() { echo -e "$(date +'%F %T') | ERROR: $*" >&2; }
trap 'err "脚本出错（行号: $LINENO）。建议查看: journalctl -u k3s -n 100 --no-pager"' ERR

# ----------------------------- 配置（可通过环境变量覆盖） -----------------------------
: "${K3S_ACTION:=reinstall}"           # reinstall|keep|exit   检测到已安装时的动作（默认重装）
: "${K3S_MIRROR:=cn}"                  # k3s 安装镜像：cn 使用 rancher-mirror.rancher.cn
: "${DEFAULT_REGISTRY:=registry.cn-hangzhou.aliyuncs.com}"  # --system-default-registry
: "${K3S_DISABLE_REGISTRY_MIRRORS:=false}"                  # true 则不写 registries.yaml
: "${ADD_COMMON_REGISTRIES:=true}"                          # 为 quay.io/ghcr.io 也写镜像（如需）
: "${DOCKER_IO_MIRRORS:=https://docker.1ms.run}"
: "${QUAY_IO_MIRRORS:=https://quay.mirrors.ustc.edu.cn}"
: "${GHCR_IO_MIRRORS:=https://ghcr.nju.edu.cn}"
: "${K3S_SELINUX_PERMISSIVE:=false}"   # true 则把 /etc/selinux/config 设为 permissive（持久）
: "${K3S_NODE_NAME:=$(hostname -s)}"
: "${KUBECONFIG_PATH:=/etc/rancher/k3s/k3s.yaml}"

REGISTRIES_CONFIG="/etc/rancher/k3s/registries.yaml"
PROFILED_FILE="/etc/profile.d/k3s-kubeconfig.sh"

log "=========================================="
log "Kubernetes 单节点集群安装（k3s，稳健版）"
log "=========================================="

# ----------------------------- 0. Root / OS / systemd 预检 -----------------------------
if [[ "${EUID:-$(id -u)}" -ne 0 ]]; then
  err "需要 root 权限。请使用: sudo bash $0"
  exit 1
fi

if ! command -v systemctl >/dev/null 2>&1; then
  err "未检测到 systemctl，当前环境不受支持（需要 systemd）。"
  exit 1
fi

if [[ -f /etc/os-release ]]; then
  # shellcheck disable=SC1091
  . /etc/os-release || true
  OS_ID=${ID:-}
  OS_VER=${VERSION_ID:-}
else
  OS_ID=""
  OS_VER=""
fi

if [[ "${OS_ID}" != "centos" && "${OS_ID}" != "rhel" ]]; then
  warn "未检测到 CentOS/RHEL（检测到: ${OS_ID:-unknown}）。脚本仅在 CentOS 7/RHEL 7 验证。"
fi

if [[ "${OS_VER%%.*}" != "7" ]]; then
  warn "建议使用 7.x 版本（当前: ${OS_VER:-unknown}）。继续尝试安装。"
fi

# ----------------------------- 1. 依赖与内核配置 -----------------------------
log "[1/6] 安装必需依赖并配置内核参数..."
# 基础工具（使用实际的包名）
YUM_PKGS=(curl ca-certificates iproute)
MISSING=()
for p in "${YUM_PKGS[@]}"; do rpm -q "$p" >/dev/null 2>&1 || MISSING+=("$p"); done
if ((${#MISSING[@]})); then
  log "安装缺失包: ${MISSING[*]}"
  yum install -y "${MISSING[@]}"
fi
log "✓ 依赖包检查完成"

# 加载内核模块
modprobe overlay || true
modprobe br_netfilter || true

# sysctl 临时与持久化
cat >/etc/sysctl.d/99-k3s.conf <<EOF
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sysctl --system >/dev/null 2>&1 || true

# SELinux
if command -v getenforce >/dev/null 2>&1; then
  SEL=$(getenforce || true)
  if [[ "$SEL" == "Enforcing" ]]; then
    warn "SELinux=Enforcing，临时设置为 Permissive 以避免容器网络问题。"
    setenforce 0 || true
    if [[ "$K3S_SELINUX_PERMISSIVE" == "true" ]] && [[ -f /etc/selinux/config ]]; then
      sed -ri 's/^SELINUX=.*/SELINUX=permissive/' /etc/selinux/config || true
      log "已将 /etc/selinux/config 设置为 permissive（持久）。"
    else
      warn "未持久修改 SELinux（设置 K3S_SELINUX_PERMISSIVE=true 可持久化）。"
    fi
  fi
fi

# ----------------------------- 2. kubectl 安装/回退 -----------------------------
log "[2/6] 检查/安装 kubectl..."
if ! command -v kubectl >/dev/null 2>&1; then
  log "尝试通过阿里云 yum 源安装 kubectl..."
  cat >/etc/yum.repos.d/kubernetes.repo <<'EOF'
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF
  if yum install -y kubectl; then
    log "✓ kubectl 安装完成 (yum)"
  else
    warn "yum 安装 kubectl 失败，将创建 k3s-kubectl 包装器。"
    cat >/usr/local/bin/kubectl <<'EOF'
#!/bin/sh
exec /usr/local/bin/k3s kubectl "$@"
EOF
    chmod +x /usr/local/bin/kubectl
    log "✓ 已创建 /usr/local/bin/kubectl 包装器（基于 k3s 内置 kubectl）"
  fi
else
  KUBECTL_VERSION=$(kubectl version --client=true 2>/dev/null || echo "已安装")
  log "✓ kubectl 已安装: ${KUBECTL_VERSION}"
fi

# ----------------------------- 3. k3s 检测/卸载/安装 -----------------------------
log "[3/6] 检查 k3s 安装状态..."
K3S_INSTALLED=false
if command -v k3s >/dev/null 2>&1 || [[ -f /usr/local/bin/k3s ]] || systemctl list-unit-files | grep -q '^k3s\.service'; then
  K3S_INSTALLED=true
fi

if [[ "$K3S_INSTALLED" == "true" ]]; then
  case "$K3S_ACTION" in
    reinstall)
      log "检测到已安装的 k3s -> 执行完全卸载和重装"
      
      # 步骤 1: 停止 k3s 服务
      log "  步骤 1/5: 停止 k3s 服务..."
      systemctl stop k3s 2>/dev/null || true
      systemctl stop k3s-agent 2>/dev/null || true
      sleep 2
      
      # 步骤 2: 执行官方卸载脚本
      log "  步骤 2/5: 执行官方卸载脚本..."
      if [[ -x /usr/local/bin/k3s-uninstall.sh ]]; then
        /usr/local/bin/k3s-uninstall.sh 2>/dev/null || true
        log "    ✓ 官方卸载脚本已执行"
      else
        log "    未找到官方卸载脚本，手动清理"
      fi
      
      # 步骤 3: 清理数据目录
      log "  步骤 3/5: 清理 k3s 数据目录..."
      rm -rf /var/lib/rancher/k3s
      rm -rf /etc/rancher/k3s
      rm -rf /var/lib/rancher/credentialprovider
      log "    ✓ 数据目录已清理"
      
      # 步骤 4: 清理 systemd 服务
      log "  步骤 4/5: 清理 systemd 服务文件..."
      rm -f /etc/systemd/system/k3s.service
      rm -f /etc/systemd/system/k3s-agent.service
      rm -f /etc/systemd/system/multi-user.target.wants/k3s.service
      rm -f /etc/systemd/system/multi-user.target.wants/k3s-agent.service
      systemctl daemon-reload
      log "    ✓ systemd 服务已清理"
      
      # 步骤 5: 清理网络接口（如果存在）
      log "  步骤 5/5: 清理网络接口..."
      ip link delete cni0 2>/dev/null || true
      ip link delete flannel.1 2>/dev/null || true
      log "    ✓ 网络接口已清理"
      
      log "✓ k3s 完全卸载完成，准备重新安装"
      ;;
    keep)
      log "检测到已安装的 k3s -> 跳过安装"
      ;;
    exit)
      err "检测到已安装的 k3s，按要求退出。设置 K3S_ACTION=reinstall 可重装。"
      exit 0
      ;;
    *)
      err "未知 K3S_ACTION=$K3S_ACTION，应为 reinstall|keep|exit"
      exit 1
      ;;
  esac
fi

if [[ "$K3S_ACTION" != "keep" ]]; then
  log "[3/6] 安装 k3s（使用国内镜像: ${K3S_MIRROR}）..."
  log "注意: 不使用 --system-default-registry 以避免 CRD 超时问题"
  log "将通过 registries.yaml 配置 Docker Hub 镜像加速"
  
  curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | \
    INSTALL_K3S_MIRROR="${K3S_MIRROR}" sh -s - \
    --node-name "${K3S_NODE_NAME}" \
    --write-kubeconfig "${KUBECONFIG_PATH}" \
    --write-kubeconfig-mode 644
  
  log "✓ k3s 安装完成"
  log "等待 k3s 启动..."
  sleep 15
else
  log "✓ 使用现有 k3s 安装"
fi

# ----------------------------- 4. kubeconfig 持久化 -----------------------------
log "[4/6] 配置 kubeconfig..."
if [[ -f "${KUBECONFIG_PATH}" ]]; then
  mkdir -p /root/.kube
  cp -f "${KUBECONFIG_PATH}" /root/.kube/config
  chmod 644 /root/.kube/config
  cat >"${PROFILED_FILE}" <<EOF
# 自动生成：k3s kubeconfig 环境变量
[ -f "${KUBECONFIG_PATH}" ] && export KUBECONFIG="${KUBECONFIG_PATH}"
EOF
  chmod 644 "${PROFILED_FILE}"
  log "✓ 已将 KUBECONFIG 持久化到 ${PROFILED_FILE}（对所有用户生效）"
else
  err "未找到 kubeconfig: ${KUBECONFIG_PATH}"
  exit 1
fi

# 如果通过 sudo 执行，也为原用户复制一份（非强制）
if [[ -n "${SUDO_USER:-}" && "${SUDO_USER}" != "root" ]]; then
  USER_HOME=$(getent passwd "$SUDO_USER" | cut -d: -f6 || echo "/home/$SUDO_USER")
  mkdir -p "$USER_HOME/.kube" && cp -f "${KUBECONFIG_PATH}" "$USER_HOME/.kube/config" && chown -R "$SUDO_USER":"$SUDO_USER" "$USER_HOME/.kube"
  log "✓ 已为用户 $SUDO_USER 写入 $USER_HOME/.kube/config"
fi

# ----------------------------- 5. registries.yaml 配置 -----------------------------
log "[5/6] 配置镜像加速（registries.yaml）..."
if [[ "${K3S_DISABLE_REGISTRY_MIRRORS}" == "true" ]]; then
  warn "已禁用镜像加速配置（K3S_DISABLE_REGISTRY_MIRRORS=true）"
else
  mkdir -p "$(dirname "${REGISTRIES_CONFIG}")"
  TS=$(date +%Y%m%d_%H%M%S)
  [[ -f "${REGISTRIES_CONFIG}" ]] && cp -f "${REGISTRIES_CONFIG}" "${REGISTRIES_CONFIG}.bak.${TS}"

  # 转换 env 中的逗号分隔为 YAML list
  to_yaml_list() {
    local IFS=','; read -r -a arr <<<"$1"; for e in "${arr[@]}"; do echo "      - \"${e}\""; done
  }

  {
    echo "mirrors:"
    echo "  \"docker.io\":"
    echo "    endpoint:"; to_yaml_list "${DOCKER_IO_MIRRORS}" 
    echo "  \"registry-1.docker.io\":"
    echo "    endpoint:"; to_yaml_list "${DOCKER_IO_MIRRORS}"
    if [[ "${ADD_COMMON_REGISTRIES}" == "true" ]]; then
      echo "  \"quay.io\":"
      echo "    endpoint:"; to_yaml_list "${QUAY_IO_MIRRORS}"
      echo "  \"ghcr.io\":"
      echo "    endpoint:"; to_yaml_list "${GHCR_IO_MIRRORS}"
    fi
  } >"${REGISTRIES_CONFIG}"

  log "✓ 写入 ${REGISTRIES_CONFIG}"
  
  # 检查 k3s 是否已经启动
  if systemctl is-active --quiet k3s; then
    log "重启 k3s 以应用 registries.yaml..."
    systemctl restart k3s
    sleep 15
    log "✓ k3s 已重启"
  else
    log "✓ registries.yaml 已配置，k3s 启动时将自动应用"
  fi
fi

# ----------------------------- 6. 等待端口与就绪 -----------------------------
log "[6/6] 等待 k3s/apiserver 就绪..."
# 等待端口监听（6443、10250）
PORT_WAIT=0
until ss -lntp 2>/dev/null | egrep -q ':(6443|10250)'; do
  ((PORT_WAIT+=2)); sleep 2; [[ $PORT_WAIT -ge 60 ]] && break
  log "  等待端口 6443/10250 监听中... (${PORT_WAIT}s)"
done
ss -lntp 2>/dev/null | egrep ':(6443|10250)' || true

# 等待 kube-apiserver readyz
READY_WAIT=0
until kubectl --request-timeout=10s get --raw='/readyz?verbose' >/dev/null 2>&1; do
  ((READY_WAIT+=3)); sleep 3; [[ $READY_WAIT -ge 90 ]] && break
  log "  等待 apiserver ready... (${READY_WAIT}s)"
  systemctl is-active --quiet k3s || { warn "k3s 服务不在运行，尝试拉起"; systemctl restart k3s; }
finally_done=true
done

# 等待节点就绪
NODE_WAIT=0
until kubectl get nodes --no-headers 2>/dev/null | grep -q " Ready "; do
  ((NODE_WAIT+=3)); sleep 3; [[ $NODE_WAIT -ge 120 ]] && break
  kubectl get nodes --no-headers 2>/dev/null | head -3 || true
  log "  等待节点 Ready... (${NODE_WAIT}s)"
done

if kubectl get nodes >/dev/null 2>&1; then
  log "\n节点状态："
  kubectl get nodes -o wide || true
else
  warn "kubectl 仍无法连接 API。"
fi

log "\n系统 Pod 状态（前 20 行）："
kubectl get pods -A 2>&1 | head -20 || true

if ! kubectl get nodes --no-headers 2>/dev/null | grep -q " Ready "; then
  warn "集群未在预期时间内完全就绪，输出诊断："
  systemctl status k3s --no-pager -l | head -60 || true
  journalctl -u k3s -n 120 --no-pager || true
  ip link show | grep -E "cni0|flannel" || warn "CNI 网桥尚未创建"
fi

log "\n=========================================="
log "k3s 安装与配置完成"
log "=========================================="
log "- kubeconfig: ${KUBECONFIG_PATH}（已写入 ${PROFILED_FILE}）"
log "- 使用示例: kubectl get nodes; kubectl get pods -A"
log "- 常用诊断: systemctl status k3s; journalctl -u k3s -f"
